
#include "main.h"


#define	wait_TIME			10
#define	wait_TIME2			10

#define	i2c_READ			0x01
#define	cmd_READ			1
#define	cmd_WRITE			0

#define gpio_SW_SDA_CLK		RCC_AHB1Periph_GPIOB
#define	gpio_SW_SDA_PORT	GPIOB
#define	gpio_SW_SDA_PIN		GPIO_Pin_9

#define gpio_SW_SCL_CLK		RCC_AHB1Periph_GPIOB
#define	gpio_SW_SCL_PORT	GPIOB
#define	gpio_SW_SCL_PIN		GPIO_Pin_6


static void TIMER_Wait_us(vu32 nCount)
{
	for (; nCount!=0; nCount--);
}

/* internal static functions */

// SDA High
static void sda_high(void)
{
	gpio_SW_SDA_PORT->BSRRL = gpio_SW_SDA_PIN;
}

// SDA low
static void sda_low(void)
{
	gpio_SW_SDA_PORT->BSRRH = gpio_SW_SDA_PIN;
}

// SCL High
static void scl_high(void)
{
	gpio_SW_SCL_PORT->BSRRL = gpio_SW_SCL_PIN;
}

// SCL low
static void scl_low(void)
{
	gpio_SW_SCL_PORT->BSRRH = gpio_SW_SCL_PIN;
}

static void sda_out(u8 out)
{
	if (out)
	{
		sda_high();
	}
	else
	{
		sda_low();
	}
}

GPIO_InitTypeDef _GPIO_SDA = {
	gpio_SW_SDA_PIN, GPIO_Mode_OUT, GPIO_Speed_50MHz, GPIO_OType_PP, GPIO_PuPd_NOPULL
};

static void sda_in_mode(void)
{
	_GPIO_SDA.GPIO_Mode  = GPIO_Mode_IN;
	GPIO_Init(gpio_SW_SDA_PORT, &_GPIO_SDA);
}

static void sda_out_mode(void)
{
	_GPIO_SDA.GPIO_Mode  = GPIO_Mode_OUT;
	GPIO_Init(gpio_SW_SDA_PORT, &_GPIO_SDA);
}

static void i2c_clk_data_out(void)
{
	scl_high();
	TIMER_Wait_us(wait_TIME);
	scl_low();
}


static void i2c_start_condition(void)
{
	sda_high();
	scl_high();
	TIMER_Wait_us(wait_TIME);

	sda_low();
	TIMER_Wait_us(wait_TIME);

	scl_low();
	TIMER_Wait_us(wait_TIME << 1);
}

static void i2c_stop_condition(void)
{
	sda_low();
	scl_high();
	TIMER_Wait_us(wait_TIME);

	sda_high();
	TIMER_Wait_us(wait_TIME);
}

static u8 i2c_check_ack(void)
{
	u32 ack = FALSE;

	sda_in_mode();
	scl_high();
	TIMER_Wait_us(wait_TIME);

	for (u32 i=10; i>0; i--)
	{
		if (~(SW_I2C_ReadVal_SDA()))
		{
			ack = TRUE;
			break;
		}
	}

	scl_low();
	sda_out_mode();
	TIMER_Wait_us(wait_TIME);

	return ack;
}

static void i2c_check_not_ack(void)
{
	sda_in_mode();
	i2c_clk_data_out();
	sda_out_mode();
	TIMER_Wait_us(wait_TIME);
}

static void i2c_slave_address(u8 IICID, u8 readwrite)
{
	if (readwrite)
	{
		IICID |= i2c_READ;
	}
	else
	{
		IICID &= ~i2c_READ;
	}

	scl_low();

	for (int x=7; x>=0; x--)
	{
		sda_out(IICID & (1 << x));
		TIMER_Wait_us(wait_TIME2);
		i2c_clk_data_out();
		TIMER_Wait_us(wait_TIME);
	}
}

static void i2c_register_address(u8 addr)
{
	scl_low();

	for (int x=7; x>=0; x--)
	{
		sda_out(addr & (1 << x));
		TIMER_Wait_us(wait_TIME2);
		i2c_clk_data_out();
		TIMER_Wait_us(wait_TIME);
	}
}

static void i2c_send_ack(void)
{
	sda_out_mode();
	sda_low();
	TIMER_Wait_us(wait_TIME);

	scl_high();
	TIMER_Wait_us(wait_TIME << 1);

	sda_low();
	TIMER_Wait_us(wait_TIME << 1);

	scl_low();
	sda_out_mode();
	TIMER_Wait_us(wait_TIME);
}

/* external functions */

void SW_I2C_Init(void)
{
	GPIO_INIT gis[] =
	{
		{gpio_SW_SDA_CLK, {gpio_SW_SDA_PIN, GPIO_Mode_OUT, GPIO_Speed_50MHz, GPIO_OType_PP, GPIO_PuPd_NOPULL}, gpio_SW_SDA_PORT,},
		{gpio_SW_SCL_CLK, {gpio_SW_SCL_PIN, GPIO_Mode_OUT, GPIO_Speed_50MHz, GPIO_OType_PP, GPIO_PuPd_NOPULL}, gpio_SW_SCL_PORT,},
	};

	Util_GpioInit(gis, _countof(gis));
}

u8 SW_I2C_ReadVal_SDA(void)
{
	return GPIO_ReadInputDataBit(gpio_SW_SDA_PORT, gpio_SW_SDA_PIN);
}

void SW_I2C_Write_Data(u8 data)
{
	scl_low();

	for (int x=7; x>=0; x--)
	{
		sda_out(data & (1 << x));
		TIMER_Wait_us(wait_TIME2);
		i2c_clk_data_out();
		TIMER_Wait_us(wait_TIME);
	}
}

u8 SW_I2C_Read_Data(void)
{
	u8 readdata = 0;

	sda_in_mode();

	for (u32 x=8; x--;)
	{
		scl_high();

		readdata <<= 1;
		if (SW_I2C_ReadVal_SDA())
			readdata |= 0x01;
		TIMER_Wait_us(wait_TIME);

		scl_low();
		TIMER_Wait_us(wait_TIME);
	}

	sda_out_mode();
	return readdata;
}

u8 SW_I2C_WriteControl_8Bit(u8 IICID, u8 regaddr, u8 data)
{
	u8 returnack = TRUE;

	i2c_start_condition();

	i2c_slave_address(IICID, cmd_WRITE);
	if (!i2c_check_ack())
	{
		returnack = FALSE;
	}
	TIMER_Wait_us(wait_TIME);

	i2c_register_address(regaddr);
	if (!i2c_check_ack())
	{
		returnack = FALSE;
	}
	TIMER_Wait_us(wait_TIME);

	SW_I2C_Write_Data(data);
	if (!i2c_check_ack())
	{
		returnack = FALSE;
	}
	TIMER_Wait_us(wait_TIME);

	i2c_stop_condition();

	return returnack;
}

u8 SW_I2C_WriteControl_16Bit(u8 IICID, u8 regaddr, u16 data)
{
	u8 returnack = TRUE;

	i2c_start_condition();

	i2c_slave_address(IICID, cmd_WRITE);
	if (!i2c_check_ack())
	{
		returnack = FALSE;
	}
	TIMER_Wait_us(wait_TIME);

	i2c_register_address(regaddr);
	if (!i2c_check_ack())
	{
		returnack = FALSE;
	}
	TIMER_Wait_us(wait_TIME);

	SW_I2C_Write_Data((data >> 8) & 0xFF);
	if (!i2c_check_ack())
	{
		returnack = FALSE;
	}
	TIMER_Wait_us(wait_TIME);

	SW_I2C_Write_Data(data & 0xFF);
	if (!i2c_check_ack())
	{
		returnack = FALSE;
	}
	TIMER_Wait_us(wait_TIME);

	i2c_stop_condition();

	return returnack;
}

u8 SW_I2C_ReadControl_8Bit(u8 IICID, u8 regaddr)
{
	u8 readdata = 0;

	i2c_start_condition();

	i2c_slave_address(IICID, cmd_WRITE);
	i2c_check_ack();
	TIMER_Wait_us(wait_TIME);

	i2c_register_address(regaddr);
	i2c_check_ack();
	TIMER_Wait_us(wait_TIME);

	i2c_start_condition();

	i2c_slave_address(IICID, cmd_READ);
	i2c_check_ack();
	TIMER_Wait_us(wait_TIME);

	readdata = SW_I2C_Read_Data();

	i2c_check_not_ack();

	i2c_stop_condition();

	return readdata;
}

u16 SW_I2C_ReadControl_16Bit(u8 IICID, u8 regaddr)
{
	u16 readdata = 0;

	i2c_start_condition();

	i2c_slave_address(IICID, cmd_WRITE);
	i2c_check_ack();

	i2c_register_address(regaddr);
	i2c_check_ack();
	TIMER_Wait_us(wait_TIME);

	i2c_start_condition();

	i2c_slave_address(IICID, cmd_READ);
	i2c_check_ack();
	TIMER_Wait_us(wait_TIME);

	readdata = SW_I2C_Read_Data();
	i2c_send_ack();
	TIMER_Wait_us(wait_TIME);

	readdata = ((readdata << 8) | SW_I2C_Read_Data());

	i2c_check_not_ack();

	i2c_stop_condition();

	return readdata;
}

